#define _CRT_SECURE_NO_WARNINGS
// ↑ 将本行在代码放在第一行可以避免 scanf的报错问题（仅在VS编译器里）
//【进阶篇】154.柔性数组
//【进阶篇】155.文件的打开
//【进阶篇】156.休息
//【进阶篇】157.文件的读写（上）
//【进阶篇】158.休息
//【进阶篇】159.文件的读写（下）


#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<errno.h>


int main()
{
	//154.柔性数组
	{
		typedef struct st_type
		{
			int i;
			int a[];
		}type_a;
		int sz = sizeof(struct st_type);
		printf("%d\n", sz);
		type_a* pa=(type_a*)malloc(sz + 40);
		if (pa == NULL)
		{
			printf("%s", strerror(errno));
			return 1;
		}
		int i = 0;
		for (i = 0; i < 10; i++)
		{
			pa->a[i] = i;
		}
		for (i = 0; i < 10; i++)
		{
			printf("%d  ", pa->a[i]);
		}
		free(pa);
		pa = NULL;

		printf("\n");
	}
	//另一种
	{
	typedef	struct S
		{
			int n;
			int* arr;
		}typeS;

	typeS* ps = (typeS*)malloc(sizeof(typeS));
	if (ps == NULL)
	{
		return 1;
	}
	ps->n = 100;
	ps->arr = (int*)malloc(40);
	if (ps == NULL)
	{
		return 1;
	}
	int* ptr = (int*)realloc(ps->arr, 80);
	if (ptr == NULL)
	{
		return 1;
	}
	ps->arr = ptr;
	free(ps->arr);
		free(ps);
		ps->arr = NULL;
		ps = NULL;
	}

	//155.文件的打开
	{
		FILE* pf =fopen("testfile.txt", "r");
		if (pf == NULL)
		{
			printf("%s\n", strerror(errno));
			return 1;
		}
		else
		{
			printf("FileOpenSuccess\n");
		}

		//关闭
		fclose(pf);
		pf = NULL;
	}
	//157.文件的读写
	{
		//只写打开
		FILE* pf = fopen("testfile.txt", "w");
		if (pf == NULL)
		{
			//printf("%s\n", strerror(errno));
			perror(fopen);//和上面的功能一致
			return 1;
		}
		else
		{
			printf("FileOpenSuccess\n");
		}
		//写入
			//fputc-字符输出（写）
		fputc('a', pf);
			//fputs-行输出（写）
		fputs("bcd\nbcd", pf);
			//fprintf-格式化输出（写）
		struct S
		{
			char arr[10];
			int age;
			float score;
		};
		struct S s = { "zhangsan",20,90.5 };
		fprintf(pf, "\n%s %d %f", s.arr, s.age, s.score);


		//关闭
		fclose(pf);
		pf = NULL;

		//只读打开
		pf = fopen("testfile.txt", "r");
		if (pf == NULL)
		{
			perror(fopen);
			return 1;
		}
		else
		{
			printf("FileOpenSuccess\n");
		}
		//读取
			//fgetc-字符输入（读）
		int ch = 0;
		while ((ch = fgetc(pf)) != '\n')
		{
			printf("%c", ch);
		}
			//fgets-行输入（读）
		char str[20] = { 0 };
		fgets(str, 5, pf);
		printf("\n%s", str);
			
			//fscanf-格式化输入（读）
		fscanf(pf, "%s %d %f", s.arr, &(s.age), &(s.score));
		printf("%s %d %f\n", s.arr, s.age, s.score);
		//关闭
		fclose(pf);
		pf = NULL;

		//fwrite-二进制输出
		struct S s2 = { "lisi",26,60.5f };
		pf = fopen("testfile.txt", "wb");
		if (pf == NULL)
		{
			
			perror(fopen);
			return 1;
		}
		fwrite(&s2, sizeof(struct S), 1, pf);
		fclose(pf);
		pf = NULL;
		//fread-二进制输入
		pf = fopen("testfile.txt", "rb");
		if (pf == NULL)
		{

			perror(fopen);
			return 1;
		}
		struct S s2r = { 0 };
		fread(&s2r, sizeof(struct S), 1, pf);
		printf("% s % d % f\n", s2r.arr, s2r.age, s2r.score);
		fclose(pf);
		pf = NULL;
	}

	//159.文件的读写
	{
		struct S
		{
			char arr[10];
			int age;
			float score;
		};
		struct S s = { "zhangsan",20,90.5 };
		char buf[100] = { 0 };
		sprintf(buf,"%s %d %f", s.arr, s.age, s.score);
		printf("%s\n", buf);
		struct S tmp = { 0 };
		sscanf(buf, "%s %d %f", tmp.arr, &(tmp.age), &(tmp.score));
		printf("%s %d %f\n", tmp.arr, tmp.age, tmp.score);

	}
	//文件的随机读写
	{
		FILE* pf = fopen("testfile.txt", "w");
		fputs("abcdefg", pf);
		fclose(pf); pf = NULL;
		pf = fopen("testfile.txt", "r");
		if (pf == NULL)
		{
			perror(fopen);
			return 1;
		}
		else
		{
			printf("FileOpenSuccess\n");
		}
		//读取
		fseek(pf, 2, SEEK_SET);
			//fgetc-字符输入（读）
		int ch = fgetc(pf);
		printf("%c\n", ch);
		printf("%d\n", ftell(pf));
		fseek(pf, 2, SEEK_CUR);
		ch = fgetc(pf);
		printf("%c\n", ch);
		printf("%d\n", ftell(pf));
		fseek(pf, -1, SEEK_END);
		ch = fgetc(pf);
		printf("%c\n", ch);
		printf("%d\n", ftell(pf));
		rewind(pf);
		printf("%d\n", ftell(pf));
		ch = fgetc(pf);
		printf("%c\n", ch);
		printf("%d\n", ftell(pf));

		//关闭
		fclose(pf);
		pf = NULL;
	}
	return 0;
}
//154.柔性数组
	// 【结构体中最后一个元素允许为大小未知的数组】
	//结构体中柔性数组前必须有别的成员 
	// sizeof含柔性数组的结构体 不计算柔性数组的大小
	// 柔性数组的结构创建变量需要用malloc进行内存动态分配，且分配的内存要大于结构体的大小 
//155.文件的打开
	//文件分为：程序文件（.c .exe .obj ...）|数据文件（程序运行时从文件读取数据或写入数据到文件）
	//文件名包含：文件路径+文件名主干+文件后缀
	//文件指针：每个使用的文件都在内存中开辟了对应文件信息区,一般创建一个FILE*的结构体指针来使用
	//打开文件：FILE* fopen (const char* filename , const char* mode);
	//			                文件名                  打开模式（r读、w写空文件、a追加、r+读写，w+读写空文件，a+读取追加）（二进制则再加一个b）
	//关闭文件：fclose
//157.文件的读写（上）
	//文件读写函数 
	// 适用于所有流（输入|输出）
		//fgetc-字符输入
		//fputc-字符输出     读取失败返回EOF
		//fgets-行输入
		//fputs-行输出       读取失败反馈NULL
		//fscanf-格式化输入
		//fprintf-格式化输出
	// 只适用于文件
		//fread-二进制输入
		//fwrite-二进制输出
	//关于输入流和输出流
		// 一个C程序，只要运行起来，就会默认打开三个流
		//FILE* stdin 标砖输入流（键盘）
		//FILE* stdout 标砖输出流（屏幕）
		//FILE* stderr 标砖错误流（屏幕）
//159.文件的读写
	//sprintf是把一个格式化的数据转换成字符串 
	//sscanf是从一个字符串数据中获取出格式化的数据 
	//文件的随机读写
		//fseek 偏移读取
		//ftell 读取偏移量计算
		//rewind 让文件指针位置回到起始位置
